﻿<title>Recording Remote Audio Streams / RecordRTC</title>
<link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
<style>
html{
	background: rgb(231, 241, 241);
	font-family: 'Open Sans', sans-serif;
    font-size: 1.2em;
    line-height: 1.5;
}
h1 {
	text-align: center;
	border-bottom: 2px solid rgb(121, 40, 40);
	color: rgb(121, 40, 40);
}
ol {
    margin-left: 4em;
}
pre {
    margin-left: 6em;
}
</style>
<h1>Recording Remote Audio Streams / <a href="https://www.webrtc-experiment.com/RecordRTC/">RecordRTC</a></h2>

<h2>issue: <a href="http://stackoverflow.com/questions/17475038/recording-a-remote-webrtc-stream-with-recordrtc/17475416#17475416" target="_blank">unable to record remote audio streams using RecordRTC.</a></h2>
<blockquote>
"opus" is the default codec on chrome;
</blockquote>

<blockquote>
Chrome uses two modes for opus....
</blockquote>

<ol>
<li>"voip" (i.e. voice mode) which is used if mono audio stream is provided by the  sender</li>
<li>"audio" which is used if stereo audio stream is provided by  the sender</li>
</ol>
<blockquote>
There is no parameter in sdp allows us control voip/audio mode for outgoing/incoming audio streams. This job can be done by sender ( I don't know how, well, still experimenting ).
</blockquote>

<blockquote>
They're using code like this:
</blockquote>

<pre>
// Default to VoIP application for mono, and AUDIO for stereo.
int application = (channels == 1) ? OPUS_APPLICATION_VOIP : OPUS_APPLICATION_AUDIO;

state->encoder = opus_encoder_create(48000, channels, application, &error);
</pre>

<blockquote>
You can see that if channels equals 1; i.e. if mono audio; they're using "voip" mode. Otherwise "audio" mode.
</blockquote>

<blockquote>
In the session description; you can see this line:
</blockquote>

<pre>
a=rtpmap:111 opus/48000/2
</pre>

<blockquote>
It is mono section with 48000 Hz (i.e. highest) clock rate.
</blockquote>

<pre>
Bitrate is 48-64 kb/s .... i.e. FB mono music
</pre>

<blockquote>
For stereo; we need:
</blockquote>

<pre>
64-128 kb/s .... i.e. FB stereo music
</pre>

<script src="https://www.webrtc-experiment.com/RecordRTC.js"></script>

<script>
    var offerer, answerer;

    window.RTCPeerConnection = window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
    window.RTCSessionDescription = window.mozRTCSessionDescription || window.RTCSessionDescription;
    window.RTCIceCandidate = window.mozRTCIceCandidate || window.RTCIceCandidate;

    navigator.getUserMedia = navigator.mozGetUserMedia || navigator.webkitGetUserMedia;
    window.URL = window.webkitURL || window.URL;

    window.iceServers = {
        iceServers: [{
                url: 'stun:23.21.150.121'
            }
        ]
    };
</script>
<script>
    /* offerer */

    function offererPeer(stream) {
        offerer = new RTCPeerConnection(window.iceServers);

        // stream is only attached by offerer
        offerer.addStream(stream);

        offerer.onicecandidate = function (event) {
            if (!event || !event.candidate) return;
            answerer.addIceCandidate(event.candidate);
        };

        offerer.createOffer(function (offer) {
            console.debug('offer sdp', offer.sdp);

            offerer.setLocalDescription(offer);
            answererPeer(offer);
        });
    }
</script>
<script>
    /* answerer */

    function answererPeer(offer) {
        answerer = new RTCPeerConnection(window.iceServers);

        // stream that is flowing from offerer toward answerer
        answerer.onaddstream = function (event) {
            console.debug('remote stream', event.stream);
			
			var recorder = new RecordRTC({
				stream: event.stream
			});
			recorder.recordAudio();
			setTimeout(function() {
				recorder.stopAudio(function() {
					var dataURL = recorder.getDataURL();
					if(dataURL.indexOf('AAAAAAAAAAAA') != -1) {
						document.querySelector('h2').innerHTML = 'Unable to record remote audio stream.<br /><br />' +
																  dataURL.substr(0, 150) + '...';
					}
					console.log(dataURL);
				});
			}, 5000);
        };

        answerer.onicecandidate = function (event) {
            if (!event || !event.candidate) return;
            offerer.addIceCandidate(event.candidate);
        };

        answerer.setRemoteDescription(offer);
        answerer.createAnswer(function (answer) {
            answerer.setLocalDescription(answer);
            offerer.setRemoteDescription(answer);
        });
    }
</script>
<script>
    function getUserMedia(callback) {
        navigator.getUserMedia({
            audio: true,
            video: false
        }, callback, onerror);

        function onerror(e) {
            console.error(e);
        }
    }
    getUserMedia(offererPeer);
</script>